"""Exploration Mode core domain models.

Initial MVP set derived from exploration_mode.md design doc.
"""

from __future__ import annotations

from datetime import datetime
from typing import Optional, Any

from sqlalchemy import (
    String,
    Integer,
    Boolean,
    DateTime,
    ForeignKey,
    Text,
    Float,
    UniqueConstraint,
    JSON,
    Index,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base


class ExplorationNode(Base):
    __tablename__ = "exploration_node"

    id: Mapped[int] = mapped_column(primary_key=True)
    code: Mapped[str] = mapped_column(String(64), unique=True, index=True)
    title: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
    short_desc: Mapped[Optional[str]] = mapped_column(String(256))
    rich_content: Mapped[Optional[str]] = mapped_column(Text)  # markdown
    level: Mapped[int] = mapped_column(Integer, default=1, index=True)
    category: Mapped[Optional[str]] = mapped_column(String(64), index=True)
    order_index: Mapped[int] = mapped_column(Integer, default=0)
    parent_id: Mapped[Optional[int]] = mapped_column(
        Integer, ForeignKey("exploration_node.id"), nullable=True, index=True
    )
    is_unlocked_default: Mapped[bool] = mapped_column(Boolean, default=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow
    )

    parent = relationship("ExplorationNode", remote_side=[id], backref="children")


class NodeEdge(Base):
    __tablename__ = "node_edge"
    id: Mapped[int] = mapped_column(primary_key=True)
    from_node_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("exploration_node.id"), index=True
    )
    to_node_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("exploration_node.id"), index=True
    )
    relation_type: Mapped[str] = mapped_column(String(32), default="related")
    weight: Mapped[int] = mapped_column(Integer, default=1)

    __table_args__ = (
        UniqueConstraint("from_node_id", "to_node_id", name="uq_node_edge"),
    )


class Quest(Base):
    __tablename__ = "quest"
    id: Mapped[int] = mapped_column(primary_key=True)
    node_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("exploration_node.id"), index=True
    )
    title: Mapped[str] = mapped_column(String(128))
    type: Mapped[str] = mapped_column(
        String(16), index=True
    )  # READ / MATCH / QNA / COLLECT
    description: Mapped[Optional[str]] = mapped_column(String(256))
    requirement_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    reward_exp: Mapped[int] = mapped_column(Integer, default=10)
    unlock_condition_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    order_index: Mapped[int] = mapped_column(Integer, default=0)

    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow
    )

    node = relationship("ExplorationNode", backref="quests")


class UserProgress(Base):
    __tablename__ = "user_progress"
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
    node_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("exploration_node.id"), index=True
    )
    unlocked: Mapped[bool] = mapped_column(Boolean, default=False)
    visit_count: Mapped[int] = mapped_column(Integer, default=0)
    last_visit_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
    exp_gained: Mapped[int] = mapped_column(Integer, default=0)

    __table_args__ = (UniqueConstraint("user_id", "node_id", name="uq_user_progress"),)


class UserQuest(Base):
    __tablename__ = "user_quest"
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
    quest_id: Mapped[int] = mapped_column(Integer, ForeignKey("quest.id"), index=True)
    status: Mapped[str] = mapped_column(
        String(16), default="PENDING", index=True
    )  # PENDING / ACTIVE / DONE / FAILED
    progress_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    started_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
    finished_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))

    __table_args__ = (UniqueConstraint("user_id", "quest_id", name="uq_user_quest"),)


class MiniGameRecord(Base):
    __tablename__ = "mini_game_record"
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
    quest_id: Mapped[Optional[int]] = mapped_column(
        Integer, ForeignKey("quest.id"), nullable=True, index=True
    )
    game_type: Mapped[str] = mapped_column(String(32), index=True)
    input_payload: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    result_payload: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    score: Mapped[int] = mapped_column(Integer, default=0)
    duration_ms: Mapped[int] = mapped_column(Integer, default=0)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow
    )


class UserProfileExt(Base):
    __tablename__ = "user_profile_ext"
    user_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("users.id"), primary_key=True
    )
    level: Mapped[int] = mapped_column(Integer, default=1)
    exp_total: Mapped[int] = mapped_column(Integer, default=0)
    next_level_exp: Mapped[int] = mapped_column(Integer, default=100)
    achievement_points: Mapped[int] = mapped_column(Integer, default=0)
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow
    )


class Achievement(Base):
    __tablename__ = "achievement"
    id: Mapped[int] = mapped_column(primary_key=True)
    code: Mapped[str] = mapped_column(String(64), unique=True, index=True)
    title: Mapped[str] = mapped_column(String(128))
    description: Mapped[Optional[str]] = mapped_column(String(256))
    condition_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
    reward_points: Mapped[int] = mapped_column(Integer, default=0)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow
    )


class UserAchievement(Base):
    __tablename__ = "user_achievement"
    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
    achievement_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("achievement.id"), index=True
    )
    earned_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint("user_id", "achievement_id", name="uq_user_achievement"),
    )


class AiLoreCache(Base):
    __tablename__ = "ai_lore_cache"
    id: Mapped[int] = mapped_column(primary_key=True)
    node_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("exploration_node.id"), index=True
    )
    locale: Mapped[str] = mapped_column(String(16), default="zh-CN")
    prompt_hash: Mapped[str] = mapped_column(String(64), index=True)
    content_md: Mapped[str] = mapped_column(Text)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint("node_id", "locale", "prompt_hash", name="uq_lore_cache_key"),
    )


# Helpful composite indexes (performance) - SQLAlchemy 2 style uses Index object
Index("ix_quest_node_type", Quest.node_id, Quest.type)
Index("ix_progress_user_node", UserProgress.user_id, UserProgress.node_id)
Index("ix_userquest_user_status", UserQuest.user_id, UserQuest.status)
